package com.example.udpscreen.runnables;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.example.udpscreen.SyncLock;
import com.example.udpscreen.enums.UdpSocketDataType;
import com.example.udpscreen.listeners.FinishListener;
import com.example.udpscreen.listeners.ProgressListener;
import com.example.udpscreen.servers.BaseSocket;
import com.example.udpscreen.utils.Convert;



public class SendFileRunnable implements Runnable{
	private final static int PART_LENGTH = 30,PACKAGE_LEN =1024 * 5;
	private final static byte CMD_RECEIPT = 0x11, CMD_PART_END = 0X10,CMD_DATA=0x0A;
	
	private String mFilePath;
	
	private BaseSocket mSocket;
	private InetAddress mRemoteAddress;
	private int mRemotePort;
	
	private List<Integer> mResendNumbers= new ArrayList<Integer>();
	
	private boolean mIsActive = true;
	private boolean mNeedReSend = false;
	
	private SyncLock lock = new SyncLock();

	private ProgressListener mProgressListener;
	//private long startDate;
	private FinishListener mFinishListener;
	private boolean mPartSendSuccess = false;
	
	
	public boolean getRunStatus(){
		return mIsActive;
	}
	
	
	public SendFileRunnable(String file_path,BaseSocket socket, ProgressListener progresslistener, FinishListener finishlistener) {
		this.mFilePath = file_path;
		this.mSocket = socket;
		this.mProgressListener = progresslistener;
		this.mFinishListener = finishlistener;
	}
	
	public void updateRemoteAddress(InetAddress address,int port){	
		this.mRemoteAddress = address;
		this.mRemotePort = port;
	}
	
	@Override
	public void run() {
		boolean issuccess = false;
		String error_content ="";
		File file = new File(mFilePath);
		
		try{
			if (!file.exists()) {
				System.out.println("send failed ! file :" + mFilePath + " not exist !");
				throw new FileNotFoundException("send failed ! file :" + mFilePath + " not exist !");
			}
			
			DatagramPacket sendPacket;
			RandomAccessFile access = new RandomAccessFile(file, "r");
			byte[] sendBuf = new byte[PACKAGE_LEN + 10]; // 5K
			
			long file_size = file.length();
			// 剩余要读取的长度
			long total = file_size;
			long send_size = 0;
			
			System.out.println("ready to send file :" + file.getName());
			while (total > 0&&mIsActive) {
				Thread.sleep(1);
				access.seek(send_size);
				for (int i = 0; i < PART_LENGTH; i++) {

					// 读取文件，返回真正读取的长度
					int rlength = access.read(sendBuf, 10, PACKAGE_LEN);

					// 将剩余要读取的长度减去本次已经读取的
					total -= rlength;
					// 序号

					if (rlength > 0) {
						sendBuf[0] = UdpSocketDataType.FILE_RESUME_RECIVE;
						sendBuf[1] = (byte) (255 - UdpSocketDataType.FILE_RESUME_RECIVE);
						sendBuf[2] =CMD_DATA;
						byte[] number_bytes = Convert.short2Bytes((short) i);
						System.arraycopy(number_bytes, 0, sendBuf, 3, number_bytes.length);
						sendPacket = new DatagramPacket(sendBuf, rlength+10, mRemoteAddress, mRemotePort);
						mSocket.sendDatagramPacket(sendPacket);
						Thread.sleep(2);
					} else
						break;
				}

				int time_out_count = 0;
			
				do {
					// send end cmd
					sendPartEnd();
					mNeedReSend = false;
					// 等待 重发
					if (lock.Wait(500)) {
						time_out_count++;
						// 超时3次结束发送
						if (time_out_count >= 20)
							throw new RuntimeException("接收超时.. 退出发送");
					}

					if (mNeedReSend) {
						// 重发方法
						if (mResendNumbers.size() > 0) {
							time_out_count = 0;
							for (int number : mResendNumbers) {
								access.seek(send_size + number * PACKAGE_LEN);
								// 读取文件，返回真正读取的长度
								int rlength = access.read(sendBuf, 10, PACKAGE_LEN);
								if (rlength > 0) {
									sendBuf[0] = UdpSocketDataType.FILE_RESUME_RECIVE;
									sendBuf[1] = (byte) (255 - UdpSocketDataType.FILE_RESUME_RECIVE);
									sendBuf[2] =CMD_DATA;
									byte[] number_bytes = Convert.short2Bytes((short) number);
									System.arraycopy(number_bytes, 0, sendBuf, 3, number_bytes.length);
									sendPacket = new DatagramPacket(sendBuf, rlength+10, mRemoteAddress,
											mRemotePort);
									mSocket.sendDatagramPacket(sendPacket);
								}
							}
						}
					}
				} while (!mPartSendSuccess&&mIsActive);
				mPartSendSuccess=false;
				send_size += PART_LENGTH * PACKAGE_LEN;
				if (mProgressListener != null)
					mProgressListener.progessChanged(send_size, file_size);

			}
			if(mIsActive){
				issuccess=true;
				error_content ="success";
			}else{
				issuccess = false;
				error_content ="用户主动关闭了发送线程";
			}
			
			
		}catch(Exception e){
			issuccess = false;
			error_content = e.getMessage();
			e.printStackTrace();
		}
		
		if (mFinishListener != null) {
//			System.out.println("timer------"+(System.currentTimeMillis()-startDate));
			mFinishListener.finish(issuccess,error_content);
		}
	}
	
	
	private static byte[] END_PART_DATA = null;

	static {
		END_PART_DATA = new byte[10];
		END_PART_DATA[0] = UdpSocketDataType.FILE_RESUME_RECIVE;
		END_PART_DATA[1] = (byte) (255 - UdpSocketDataType.FILE_RESUME_RECIVE);
		END_PART_DATA[2] = CMD_PART_END;
	}

	private void sendPartEnd() {

		DatagramPacket packet = new DatagramPacket(END_PART_DATA, END_PART_DATA.length, mRemoteAddress, mRemotePort);
		try { 
			Thread.sleep(1);
			mSocket.sendDatagramPacket(packet);
			System.out.println("sendPartEnd");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	public void dealWithData(byte[] data,int real_len) {
		int type = data[2];
		if (type == CMD_RECEIPT) {
			String json_str = new String(data, 10, real_len - 10);
			System.out.println("dealWithData - string" +json_str);
			try {
				JSONObject json = new JSONObject(json_str);
				boolean need_resend = json.getBoolean("need_resend");
				if (need_resend) {
					JSONArray numbers = json.getJSONArray("numbers");
					mResendNumbers = new ArrayList<Integer>();
					for (int i = 0; i < numbers.length(); i++) {
						mResendNumbers.add(numbers.getInt(i));
					}
				}else{
					mPartSendSuccess=true;
				}
				mNeedReSend = need_resend;
				lock.Notify();
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}

	}

	
	public void onDestory(){
		lock.Notify();
		mIsActive =false;
	}
}
